home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / Pascal / Source□ / Talk Source / TCP Libraries / TCPStuff.unit < prev    next >
Encoding:
Text File  |  1992-04-20  |  21.8 KB  |  752 lines  |  [TEXT/PJMM]

  1. unit TCPStuff;
  2.  
  3. { This program was written by Peter N Lewis, Mar 1992 in THINK Pascal 4.0.1 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         TCPTypes;
  9.  
  10.     const
  11.         Minimum_TCPBUFFERSIZE = 4096;
  12.         Default_TCPBUFFERSIZE = longInt(6) * 1024;
  13.     { Amount of space to allocate for each TCP connection }
  14.         INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  15.         control_block_max = 260;
  16.         tooManyControlBlocks = -23098;
  17.  
  18.     type
  19.         OSErrPtr = ^OSErr;
  20.  
  21. { TCP connection description: }
  22.         TCPConnectionType = record
  23.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  24.                 stream: StreamPtr;
  25.                 asends, asendcompletes: longInt;
  26.                 closedone: boolean;
  27.                 closeuserptr: OSErrPtr;
  28.                 incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  29.                 incomingSize: longInt;                        { Number of bytes left in inBuf. }
  30.                 buffer: ptr;        { connection buffer. }
  31.                 inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  32.             end;
  33.         TCPConnectionPtr = ^TCPConnectionType;
  34.  
  35.         MyControlBlock = record
  36.                 tcp: TCPControlBlock;
  37.                 inuse: boolean;
  38.                 userptr: OSErrPtr;
  39.                 proc: procPtr;
  40.                 tcpc: TCPConnectionPtr;
  41.             end;
  42.         MyControlBlockPtr = ^MyControlBlock;
  43.  
  44.  
  45.         TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  46.             T_Closing, T_PleaseClose, T_Unknown);
  47.  
  48.     function TCPNameToAddr (var hostName: str255; timeout: longInt; var hostFile: str255): longInt;
  49.     function TCPOpenResolver (var hostFile: str255; var dataptr: ptr): OSErr;
  50.     function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  51.     procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  52.     function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  53.     procedure TCPCloseResolver (dataptr: ptr);
  54.  
  55.     function C2PStr (s: stringPtr): stringPtr;
  56.     procedure SanitizeHostName (var s: str255);
  57.  
  58.     function TCPInit: OSErr;
  59.     procedure TCPFinish;
  60.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  61.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  62.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  63.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  64.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  65.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  66.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  67.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  68.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  69.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  70.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  71.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  72. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  73.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  74.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  75.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  76.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  77.                                     var gottermchar: boolean): OSErr;
  78.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  79.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  80.  
  81. implementation
  82.  
  83. {    Loosely based on code by Harry Chesley 12/88, thus Copyright © 1988 Apple Computer, Inc.}
  84. {    Converted to sensible pascal interface 7/91 by Peter Lewis, thus also Copyright © 1991 Peter Lewis }
  85.  
  86.     const
  87.         MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  88.         dispose_block_max = 100;
  89.  
  90.     type
  91.         MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  92.  
  93.     var
  94.         driver_refnum: integer;
  95.         controlblocks: MyControlBlockArray;
  96.         max_dispose_block: integer;
  97.         disposeblocks: array[1..dispose_block_max] of ptr;
  98.  
  99.     procedure SanitizeHostName (var s: str255);
  100.         var
  101.             dummysp: stringPtr;
  102.     begin
  103.         dummysp := C2PStr(@s);
  104. {$PUSH}
  105. {$R-}
  106.         if s[Length(s)] = '.' then
  107.             s[0] := chr(Length(s) - 1);
  108. {$POP}
  109.     end;
  110.  
  111.     function GetA6: ptr;
  112.     inline
  113.         $2F4E, $0000;
  114.  
  115.     procedure CallCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  116.     inline
  117.         $205F, $4E90;
  118.  
  119. {$PUSH}
  120. {$D-}
  121.     procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  122.         type
  123.             stackframe = packed record
  124.                     frameptr: ptr;
  125.                     returnptr: ptr;
  126.                     paramblockptr: MyControlBlockPtr;
  127.                 end;
  128.             stackframeptr = ^stackframe;
  129.         var
  130.             a6: stackframeptr;
  131.             cbp: MyControlBlockPtr;
  132.     begin
  133.         a6 := stackframeptr(GetA6);
  134.         cbp := a6^.paramblockptr;
  135.         with cbp^ do begin
  136.             if userptr <> nil then
  137.                 userptr^ := cbp^.tcp.ioResult;
  138.             inuse := false;
  139.             if proc <> nil then
  140.                 CallCompletion(cbp, proc);
  141.         end;
  142.     end;
  143.  
  144.     procedure ZotBlocks;
  145.     begin
  146.         while max_dispose_block > 0 do begin
  147.             DisposPtr(disposeblocks[max_dispose_block]);
  148.             max_dispose_block := max_dispose_block - 1;
  149.         end;
  150.     end;
  151.  
  152.     procedure AddBlock (p: univ ptr);
  153.     begin
  154.         if max_dispose_block < dispose_block_max then begin
  155.             max_dispose_block := max_dispose_block + 1;
  156.             disposeblocks[max_dispose_block] := p;
  157.         end;
  158.     end;
  159.  
  160.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  161.     { Zero out the control block parameters. }
  162.         var
  163.             i: integer;
  164.             p: longInt;
  165.     begin
  166.         ZotBlocks;
  167.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  168.             ptr(p)^ := 0;
  169.         cb.tcpStream := stream;
  170.         cb.ioCRefNum := driver_refnum;
  171.         cb.csCode := call;
  172.     end;
  173.  
  174.     function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  175. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  176.         var
  177.             i: integer;
  178.     begin
  179.         i := 1;
  180.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  181.             i := i + 1;
  182.         cbp := controlblocks[i];
  183.         if cbp = nil then begin
  184.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  185.             if cbp <> nil then begin
  186.                 cbp^.inuse := false;
  187.                 controlblocks[i] := cbp;
  188.             end;
  189.         end;
  190.         if (cbp <> nil) & not cbp^.inuse then begin
  191.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  192.             cbp^.tcp.ioCompletion := @IOCompletion;
  193.             cbp^.inuse := true;
  194.             cbp^.userptr := userptr;
  195.             cbp^.tcpc := tcpc;
  196.             cbp^.proc := proc;
  197.             if userptr <> nil then
  198.                 userptr^ := inprogress;
  199.             GetCB := noErr;
  200.         end
  201.         else begin
  202.             cbp := nil;
  203.             GetCB := memFullErr;
  204.         end;
  205.     end;
  206.  
  207.     procedure FreeCB (var cbp: MyControlBlockPtr);
  208.     begin
  209.         if cbp <> nil then
  210.             cbp^.inuse := false;
  211.         cbp := nil;
  212.     end;
  213. {$POP}
  214.  
  215. {$S Init}
  216.     function TCPInit: OSErr;
  217.         var
  218.             oe: OSErr;
  219.             i: integer;
  220.     begin
  221.         max_dispose_block := 0;
  222.         oe := OpenDriver('.IPP', driver_refnum);
  223.         for i := 1 to control_block_max do
  224.             controlblocks[i] := nil;
  225.         TCPInit := oe;
  226.     end;
  227.  
  228. {$S Term}
  229.     procedure TCPFinish;
  230.         var
  231.             i: integer;
  232.     begin
  233.         for i := 1 to control_block_max do
  234.             if controlblocks[i] <> nil then begin
  235.                 DisposPtr(ptr(controlblocks[i]));
  236.                 controlblocks[i] := nil;
  237.             end;
  238.     end;
  239.  
  240. {$S}
  241.     procedure DestroyConnection (var connection: TCPConnectionPtr);
  242.     begin
  243.         connection^.magic := '????';
  244.         if connection^.buffer <> nil then
  245.             DisposPtr(ptr(connection^.buffer));
  246.         DisposPtr(Ptr(connection));
  247.         connection := nil;
  248.     end;
  249.  
  250.     function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  251.     begin
  252.         if connection = nil then
  253.             ValidateConnection := connectionDoesntExist
  254.         else if connection^.magic <> MAGICNUMBER then
  255.             ValidateConnection := connectionDoesntExist
  256.         else
  257.             ValidateConnection := noErr;
  258.     end;
  259.  
  260.     function PBControlSync (var cb: TCPControlBlock): OSErr;
  261.     begin
  262.         PBControlSync := PBControl(@cb, false);
  263.     end;
  264.  
  265. {$PUSH}
  266. {$D-}
  267.     function PBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  268.         var
  269.             oe: OSErr;
  270.     begin
  271.         oe := PBControl(ParmBlkPtr(cbp), true);
  272.         if oe <> noErr then
  273.             FreeCB(cbp);
  274.         PBControlAsync := oe;
  275.     end;
  276. {$POP}
  277.  
  278.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  279.         var
  280.             cb: TCPControlBlock;
  281.             oe: OSErr;
  282.     begin
  283.         ZeroCB(cb, nil, TCPcsGetMyIP);
  284.         oe := PBControlSync(cb);
  285.         myIP := cb.getmyip.ourAddress;
  286.         TCPGetMyIPAddr := oe;
  287.     end;
  288.  
  289.     procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  290.     begin
  291.         if userptr <> nil then begin
  292.             if oe <> noErr then
  293.                 userptr^ := oe;
  294.         end;
  295.     end;
  296.  
  297.     function CreateStream (var connection: TCPConnectionPtr; buffersize: longInt): OSErr;
  298.         var
  299.             oe: OSErr;
  300.             cb: TCPControlBlock;
  301.     begin
  302.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  303.         if connection = nil then
  304.             oe := memFullErr
  305.         else
  306.             with connection^ do begin
  307.                 buffer := NewPtr(buffersize);
  308.                 if buffer = nil then begin
  309.                     oe := memFullErr;
  310.                     DisposPtr(ptr(connection));
  311.                     connection := nil;
  312.                 end
  313.                 else begin
  314.                     magic := MAGICNUMBER;
  315.                     asends := 0;
  316.                     asendcompletes := 0;
  317.                     closedone := false;
  318.                     incomingSize := 0;
  319.                     ZeroCB(cb, nil, TCPcsCreate);
  320.                     cb.create.rcvBuff := buffer;
  321.                     cb.create.rcvBuffLen := buffersize;
  322.                     oe := PBControlSync(cb);
  323.                     stream := cb.tcpStream;
  324.                 end;
  325.             end;
  326.         if (oe <> noErr) and (connection <> nil) then
  327.             DestroyConnection(connection);
  328.         CreateStream := oe;
  329.     end;
  330.  
  331.     function PAOpen (var connection: TCPConnectionPtr; cs: integer; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  332.         var
  333.             oe, ooe: OSErr;
  334.             cbp: MyControlBlockPtr;
  335.             cb: TCPControlBlock;
  336.     begin
  337.         oe := CreateStream(connection, buffersize);
  338.         if oe = noErr then begin
  339.             with connection^ do begin
  340.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  341.                 if oe = noErr then begin
  342.                     cbp^.tcp.open.localPort := localPort;
  343.                     cbp^.tcp.open.remoteHost := remoteIP;
  344.                     cbp^.tcp.open.remotePort := remoteport;
  345.                     oe := PBControlAsync(cbp);
  346.                 end;
  347.                 if oe <> noErr then begin
  348.                     ZeroCB(cb, stream, TCPcsRelease);
  349.                     ooe := PBControlSync(cb);
  350.                     DestroyConnection(connection);
  351.                 end;
  352.             end;
  353.         end;
  354.         SetUserPtr(userptr, oe);
  355.         PAOpen := oe;
  356.     end;
  357.  
  358. { Open a connection to another machine }
  359.     function TCPActiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  360.     begin
  361.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  362.     end;
  363.  
  364. { Open a socket on this machine, to wait for a connection }
  365.     function TCPPassiveOpen (var connection: TCPConnectionPtr; buffersize: longInt; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  366.     begin
  367.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, buffersize, localport, remoteIP, remoteport, userptr);
  368.     end;
  369.  
  370.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  371. { Return readCount characters from the TCP connection. }
  372. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  373.         var
  374.             cb: TCPControlBlock;
  375.             oe: OSErr;
  376.     begin
  377.         repeat
  378.             ZeroCB(cb, connection^.stream, TCPcsRcv);
  379.             cb.receive.rcvBuff := returnPtr;
  380.             cb.receive.rcvBuffLength := readCount;
  381.             oe := PBControlSync(cb);
  382.             longInt(returnPtr) := longInt(returnPtr) + cb.receive.rcvBuffLength;
  383.             readCount := readCount - cb.receive.rcvBuffLength;
  384.         until (oe <> noErr) or (readCount = 0);
  385.         TCPRawReceiveChars := oe;
  386.     end;
  387.  
  388. { Return readCount characters from the TCP connection.}
  389.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  390.         var
  391.             readCountStr: Str255;
  392.             l: longInt;
  393.             p: Ptr;
  394.             oe: OSErr;
  395.             cb: TCPControlBlock;
  396.     begin
  397.         oe := ValidateConnection(connection);
  398.         if oe = noErr then
  399.             if readCount < 0 then
  400.                 oe := invalidLength
  401.             else if readCount > 0 then begin
  402.                 p := returnPtr;
  403.                 with connection^ do
  404.                     if incomingSize > 0 then begin
  405.             { Read as much as there is or as much as we need, whichever is less. }
  406.                         if readCount < incomingSize then
  407.                             l := readCount
  408.                         else
  409.                             l := incomingSize;
  410.                         BlockMove(incomingPtr, p, l);
  411.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  412.                         incomingSize := incomingSize - l;
  413.                         p := Ptr(ord4(p) + l);
  414.                         readCount := readCount - l;
  415.                     end;
  416.                 { If there's more needed, then read it from the connection. }
  417.                 if readCount > 0 then begin
  418.                         { Issue a read and wait until it all arrives). }
  419.                     oe := TCPRawReceiveChars(connection, p, readCount);
  420.                 end;
  421.             end;
  422.         TCPReceiveChars := oe;
  423.     end;
  424.  
  425.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  426.         { Return the next byte in the buffer, reading more in if necessary. }
  427.         var
  428.             waitUntil: longInt;
  429.             readIn: longInt;
  430.             oe: OSErr;
  431.             cb: TCPControlBlock;
  432.     begin
  433.         oe := ValidateConnection(connection);
  434.         if oe = noErr then
  435.             with connection^ do begin            { Check if we need to read in more bytes. }
  436.                 if incomingSize = 0 then begin
  437.                     if timeout = 0 then
  438.                         oe := commandTimeout
  439.                     else begin
  440.                         waitUntil := TickCount + timeout;
  441.     { keep on trying to read until we get at least one, or the time-out happens. }
  442.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  443.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  444.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  445.                                 if readIn > INCOMINGBUFSIZE then
  446.                                     readIn := INCOMINGBUFSIZE;
  447.                         { Issue the read. }
  448.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  449.                                 if oe = noErr then begin
  450.                                     incomingSize := readIn;
  451.                                     incomingPtr := @inBuf;
  452.                                 end;
  453.                             end        { If not, do another round or get out, depending on the timeout condition. }
  454.                             else if TickCount > waitUntil then begin
  455.                                 oe := commandTimeOut;
  456.                             end;
  457.                         end;
  458.                     end;
  459.                 end;
  460.                 { Get the byte to return. }
  461.                 if incomingSize > 0 then begin
  462.                     b := incomingPtr^;
  463.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  464.                     incomingSize := incomingSize - 1;
  465.                 end
  466.                 else
  467.                     b := 0;
  468.             end;
  469.         TCPReadByte := oe;
  470.     end;
  471.  
  472. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  473. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  474. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  475. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  476. { will read the entire buffer, and any characters that arrive before a timeout }
  477.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  478.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  479.                                     var gottermchar: boolean): OSErr;
  480.         var
  481.             oe: OSErr;
  482.  
  483.         procedure putByte (b: signedByte);
  484.         { Put the byte b after the output handle, increasing the handle's size in the process. }
  485.             var
  486.                 p: Ptr;
  487.         begin
  488.             p := Ptr(ord4(readPtr) + readPos);
  489.             p^ := b;
  490.             readPos := readPos + 1;
  491.         end;
  492.  
  493.         var
  494.             inChar: SignedByte;
  495.  
  496.     begin
  497.         oe := ValidateConnection(connection);
  498.         gottermchar := false;
  499. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  500.         while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  501.             oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  502.             if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  503.                 putByte(inChar);                    { Check for the end. }
  504.                 gottermchar := inChar = termChar;
  505.             end;
  506.         end;
  507.         if oe = commandTimeOut then
  508.             oe := noErr;
  509.         TCPReceiveUpTo := oe;
  510.     end;
  511.  
  512.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  513.         var
  514.             wds: wdsType;
  515.             oe: OSErr;
  516.             cb: TCPControlBlock;
  517.             p: ptr;
  518.     begin
  519.         oe := ValidateConnection(connection);
  520.         if oe = nOErr then
  521.             if writeCount > 0 then begin
  522.                 wds.buffer := writePtr;
  523.                 wds.size := writeCount;
  524.                 wds.term := 0;
  525.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  526.                 cb.send.wds := @wds;
  527.                 oe := PBControlSync(cb);
  528.             end
  529.             else if writeCount < 0 then
  530.                 oe := InvalidLength;
  531.         TCPSend := oe;
  532.     end;
  533.  
  534. {$PUSH}
  535. {$D-}
  536.     procedure TCPSendComplete (cbp: MyControlBlockPtr);
  537.         var
  538.             oe: OSErr;
  539.     begin
  540.         AddBlock(cbp^.tcp.send.wds);
  541.         with cbp^.tcpc^ do begin
  542.             asendcompletes := asendcompletes + 1;
  543.             if (asendcompletes = asends) and closedone then begin
  544.                 asendcompletes := asendcompletes - 1; { Avoid race condition with TCPClose }
  545.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  546. { GetCB won't NewPtr because the completion has just released a block }
  547.                 if oe = noErr then
  548.                     oe := PBControlAsync(cbp);
  549.             end;
  550.         end;
  551.     end;
  552. {$POP}
  553.  
  554.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  555.         type
  556.             myblock = record
  557.                     wds: wdsType;
  558.                     data: array[0..100] of byte;
  559.                 end;
  560.             myblockptr = ^myblock;
  561.         var
  562.             oe: OSErr;
  563.             cbp: MyControlBlockPtr;
  564.             p: myblockptr;
  565.     begin
  566.         oe := ValidateConnection(connection);
  567.         if oe = nOErr then
  568.             if writeCount > 0 then begin
  569.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  570.                 if p = nil then
  571.                     oe := memFullErr
  572.                 else begin
  573.                     p^.wds.buffer := @p^.data;
  574.                     p^.wds.size := writeCount;
  575.                     p^.wds.term := 0;
  576.                     with p^.wds do
  577.                         BlockMove(writePtr, buffer, size);
  578.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  579.                     cbp^.tcp.send.wds := POINTER(p);
  580.                     with connection^ do
  581.                         asends := asends + 1;
  582.                     oe := PBControlAsync(cbp);
  583.                     if oe <> noErr then
  584.                         DisposPtr(ptr(p));
  585.                 end;
  586.             end
  587.             else if writeCount < 0 then
  588.                 oe := InvalidLength;
  589.         TCPSendAsync := oe;
  590.     end;
  591.  
  592.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  593.         var
  594.             oe: OSErr;
  595.             cbp: MyControlBlockPtr;
  596.     begin
  597.         oe := ValidateConnection(connection);
  598.         if oe = noErr then
  599.             with connection^ do begin
  600.                 closeuserptr := userptr;
  601.                 if userptr <> nil then
  602.                     userptr^ := inProgress;
  603.                 closedone := true;
  604.                 if asends = asendcompletes then begin
  605.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  606.                     if oe = noErr then begin
  607.                         oe := PBControlAsync(cbp);
  608.                     end;
  609.                 end;
  610.             end;
  611.         SetUserPtr(userptr, oe);
  612.         TCPClose := oe;
  613.     end;
  614.  
  615.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  616.         var
  617.             oe: OSErr;
  618.             cb: TCPControlBlock;
  619.     begin
  620.         oe := ValidateConnection(connection);
  621.         if oe = noErr then begin
  622.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  623.             oe := PBControlSync(cb);
  624.         end;
  625.         TCPAbort := oe;
  626.     end;
  627.  
  628. { Release the TCP stream, including the buffer.}
  629.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  630.         var
  631.             oe: OSErr;
  632.             cb: TCPControlBlock;
  633.     begin
  634.         oe := ValidateConnection(connection);
  635.         if oe = noErr then begin
  636.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  637.             oe := PBControlSync(cb);
  638.             DestroyConnection(connection);
  639.         end;
  640.         TCPRelease := oe;
  641.     end;
  642.  
  643. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  644.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  645.         var
  646.             cb: TCPControlBlock;
  647.             oe: OSErr;
  648.     begin
  649.         oe := ValidateConnection(connection);
  650.         localhost := 0;
  651.         localport := 0;
  652.         remotehost := 0;
  653.         remoteport := 0;
  654.         available := 0;
  655.         if oe <> noErr then begin
  656.             state := 99; { Error -> Closed }
  657.         end
  658.         else begin
  659.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  660.             if PBControlSync(cb) <> noErr then begin
  661.                 state := 99; { Closed }
  662.             end
  663.             else begin
  664.                 state := cb.status.connectionState;
  665.                 localhost := cb.status.localhost;
  666.                 localport := cb.status.localport;
  667.                 remotehost := cb.status.remotehost;
  668.                 remoteport := cb.status.remoteport;
  669.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  670.             end;
  671.         end;
  672.     end;
  673.  
  674. { Return the state of the TCP connection.}
  675.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  676.         var
  677.             state: integer;
  678.             localhost: longInt;
  679.             localport: integer;
  680.             remotehost: longInt;
  681.             remoteport: integer;
  682.             available: longInt;
  683.     begin
  684.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  685.         case state of
  686.             0: 
  687.                 TCPState := T_Closed;
  688.             2: 
  689.                 TCPState := T_Listening;
  690.             4, 6: 
  691.                 TCPState := T_Opening;
  692.             8: 
  693.                 TCPState := T_Established;
  694.             10, 12, 16, 18, 20: 
  695.                 TCPState := T_Closing;
  696.             14: 
  697.                 TCPState := T_PleaseClose;
  698.             98: 
  699.                 TCPState := T_WaitingForOpen;
  700.             99: 
  701.                 TCPState := T_Closed;
  702.             otherwise
  703.                 TCPState := T_Unknown;
  704.         end;
  705.     end;
  706.  
  707. {    Return the number of characters available for reading from the TCP connection.}
  708.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  709.         var
  710.             state: integer;
  711.             localhost: longInt;
  712.             localport: integer;
  713.             remotehost: longInt;
  714.             remoteport: integer;
  715.             available: longInt;
  716.     begin
  717.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  718.         TCPCharsAvailable := available;
  719.     end;
  720.  
  721.     function TCPLocalPort (connection: TCPConnectionPtr): integer;
  722.         var
  723.             state: integer;
  724.             localhost: longInt;
  725.             localport: integer;
  726.             remotehost: longInt;
  727.             remoteport: integer;
  728.             available: longInt;
  729.     begin
  730.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  731.         TCPLocalPort := localport;
  732.     end;
  733.  
  734.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  735.         var
  736.             buffer: array[0..255] of signedByte;
  737.             f: longInt;
  738.             oe: OSErr;
  739.     begin
  740.         f := TCPCharsAvailable(connection);
  741.         oe := noErr;
  742.         while (f > 0) and (oe = noErr) do begin
  743.             if f > 256 then
  744.                 f := 256;
  745.             oe := TCPReceiveChars(connection, @buffer, f);
  746.             if oe = noErr then
  747.                 f := TCPCharsAvailable(connection);
  748.         end;
  749.         TCPFlush := oe;
  750.     end;
  751.  
  752. end.